#include "GameController.h"

namespace TUMAR { namespace CountryRoads {



	MarkerHistory::MarkerHistory(void): X(-1), Y(-1), Timestamp(0)
	{
	}
	
	MarkerHistory::~MarkerHistory(void)
	{
	}

	bool MarkerHistory::HasChanged(int x, int y)
	{
		return this->X != x || this->Y != y;
	}

	void MarkerHistory::Update(int x, int y)
	{
		if(!this->HasChanged(x, y))
			return;

		this->X = x;
		this->Y = y;
		this->Timestamp = cv::getTickCount();
	}

	bool MarkerHistory::LockIntervalExpired(double interval)
	{
		return (((double)cv::getTickCount() - Timestamp) / cv::getTickFrequency()) >= interval;
	}

	StartMovementTimer::StartMovementTimer(GameController* controller) : controller(controller)
	{
	}

	StartMovementTimer::~StartMovementTimer(void)
	{
	}

	void StartMovementTimer::Tick(void) const
	{
		this->controller->StartMoveCharacter();
	}

	MovementTimer::MovementTimer(GameController* controller) : controller(controller)
	{
	}

	MovementTimer::~MovementTimer(void)
	{
	}

	void MovementTimer::Tick(void) const
	{
		this->controller->MoveCharacter();
	}

	GameController::GameController(Game* game, GameBoard* board, MarkerEnvironment* markerEnvironment) : game(game), board(board), markerEnvironment(markerEnvironment), startTimer(this), timer(this)
	{
	}
	
	GameController::~GameController(void)
	{
	}

	void GameController::OnMarkersFound(const MarkerContainer& markers)
	{
		this->board->UserTiles.clear();
		this->markerEnvironment->Update(markers, this->board->GetWidth(), this->board->GetHeight());
		
		// update game board
		if(this->markerEnvironment->IsBoardVisible() && this->game->State == Running)
		{
			this->PrintTimeLeft();

			const std::vector<std::tuple<const Marker*, int, int>> markers = this->markerEnvironment->GetInBoardMarkers();

			// update markers, which are in the game board area
			for(std::vector<std::tuple<const Marker*, int, int>>::const_iterator it = markers.begin(); it != markers.end(); it++)
			{
				const Marker* m = std::get<0>(*it);
				int x = std::get<1>(*it);
				int y = std::get<2>(*it);

				Tile* t = this->board->GetTile(x, y);

				// track marker position
				this->history[m->MarkerId].Update(x, y);

				// create user tile
				UserTile tile;
				tile.Position[0] = x;
				tile.Position[1] = y;
				tile.Orientation = m->MarkerRotation;
				tile.SetType(this->markerEnvironment->GetTileType(m));
				tile.Conflict = t->GetType() !=  Empty;
				
				// lock tile, if marker hasn't moved
				if(!tile.Conflict && this->history[m->MarkerId].LockIntervalExpired(this->game->TileLockTimeout))
				{
					t->Orientation = tile.Orientation;
					t->SetType(tile.GetType());

					tile.Conflict = true;
				}

				this->board->UserTiles.push_back(tile);
			}
		}

		this->GameUpdated(this->game, this->board, this->markerEnvironment);
	}

	void GameController::OnKeyPressed(unsigned char key)
	{
		int _key = key;

		switch(_key)
		{
		case 13:
			if(this->game->State == Waiting)
				this->StartGame();
			break;
		case 32:
			this->InitGame();
			break;
		default:
			std::cout << _key << std::endl;
			break;
		}
	}
	
	void GameController::InitGame(void)
	{
		std::cout << "-- Init Game --" << std::endl;

		this->history.clear();
		this->board->Reset();

		GameBoardGenerator generator(this->board);
		generator.GenerateStartAndEnd();
		generator.GenerateBorder();
		generator.GenerateObstacles();

		this->timer.Stop();
		this->startTimer.Stop();
		this->game->State = Waiting;
	}

	void GameController::StartGame(void)
	{
		this->CheckGameState(Waiting);
		
		std::cout << "-- Start Game --" << std::endl;

		this->startTimer.Schedule(this->game->MovementStartTimeout * 1000.0);
		this->game->StartTime = cv::getTickCount();
		this->game->State = Running;
	}

	void GameController::FinishWonGame(void)
	{
		this->CheckGameState(Running);
		
		std::cout << std::endl << "-- Won Game --" << std::endl;

		this->timer.Stop();
		this->game->State = Won;
	}

	void GameController::FinishLostGame(void)
	{
		this->CheckGameState(Running);
		
		std::cout << std::endl << "-- Lost Game --" << std::endl;

		this->timer.Stop();
		this->game->State = Lost;
	}

	void GameController::StartMoveCharacter(void)
	{
		this->CheckGameState(Running);

		std::cout << std::endl << "-- Start Movement --" << std::endl;

		this->startTimer.Stop();
		this->timer.Schedule(game->MovementTimeout * 1000.0);
		this->game->LastMovementTime = cv::getTickCount();
	}

	void GameController::MoveCharacter(void)
	{
		this->CheckGameState(Running);
		//let Lucky walk along the path

			//retrieve the current position and orientation of the character
			//pretend to walk one step in the current direction
			//compare the new tile with the current position
			//does the tile and it's rotation match the requirement->move character(don't forget to set orientation!):drown character
		Character* prospectiveCharacter=new TUMAR::CountryRoads::Character();
		prospectiveCharacter->initPosition(this->board->Character->Position[0],this->board->Character->Position[1],this->board->Character->Orientation);

		switch(this->board->Character->Orientation)
		{
		case 0:
			prospectiveCharacter->Position[1]--;
			break;
		case 1:
			prospectiveCharacter->Position[0]++;
			break;
		case 2:
			prospectiveCharacter->Position[1]++;
			break;
		case 3:
			prospectiveCharacter->Position[0]--;
			break;
		default:
			break;
		}

		//time delay to see move...
		Tile* newTile=board->GetTile(prospectiveCharacter->Position[0],prospectiveCharacter->Position[1]);
		if(newTile->Position[0]==this->board->EndTile->Position[0] && newTile->Position[1]==this->board->EndTile->Position[1]){
			if(prospectiveCharacter->Orientation%2==newTile->Orientation%2){
				this->board->Character=prospectiveCharacter;
				this->FinishWonGame();
			}
			else{
				this->FinishLostGame();
			}
		}
		else {
			switch(newTile->GetType())
			{
			case RoadStraight:
				if(prospectiveCharacter->Orientation%2==newTile->Orientation%2){
					this->board->Character=prospectiveCharacter;
					//this->MoveCharacter();
				}
				else{
					this->FinishLostGame();
				}
				break;
			case RoadCurve:
				switch(prospectiveCharacter->Orientation)
				{
				case 0:
					switch(newTile->Orientation)
					{
					case 1:
							prospectiveCharacter->Orientation=1;
						this->board->Character=prospectiveCharacter;
						//this->MoveCharacter();
						break;
					case 2:
						prospectiveCharacter->Orientation=3;
						this->board->Character=prospectiveCharacter;
						//this->MoveCharacter();
						break;
					//case 0:
					//	prospectiveCharacter->Orientation=1;
					//	this->board->Character=prospectiveCharacter;
					//	//this->MoveCharacter();
					//	break;
					//case 1:
					//	prospectiveCharacter->Orientation=3;
					//	this->board->Character=prospectiveCharacter;
					//	//this->MoveCharacter();
					//	break;
					default:
						this->FinishLostGame();
						break;
					}
					break;
				case 1:
					switch(newTile->Orientation)
					{
					//case 0:
					//	prospectiveCharacter->Orientation=2;
					//	this->board->Character=prospectiveCharacter;
					//	//this->MoveCharacter();
					//	break;
						case 2:
						prospectiveCharacter->Orientation=2;
						this->board->Character=prospectiveCharacter;
						//this->MoveCharacter();
						break;
					case 3:
						prospectiveCharacter->Orientation=0;
						this->board->Character=prospectiveCharacter;
						//this->MoveCharacter();
						break;
					default:
						this->FinishLostGame();
						break;
					}
					break;
				case 2:
					switch(newTile->Orientation)
					{
					//case 2:
					//	prospectiveCharacter->Orientation=1;
					//	this->board->Character=prospectiveCharacter;
					//	//this->MoveCharacter();
					//	break;
						case 0:
						prospectiveCharacter->Orientation=1;
						this->board->Character=prospectiveCharacter;
						//this->MoveCharacter();
						break;
					case 3:
						prospectiveCharacter->Orientation=3;
						this->board->Character=prospectiveCharacter;
						//this->MoveCharacter();
						break;
					default:
						this->FinishLostGame();
						break;
					}
					break;
				case 3:
					switch(newTile->Orientation)
					{
						case 0:
						prospectiveCharacter->Orientation=0;
						this->board->Character=prospectiveCharacter;
						//this->MoveCharacter();
						break;
					case 1:
						prospectiveCharacter->Orientation=2;
						this->board->Character=prospectiveCharacter;
						//this->MoveCharacter();
						break;
					//case 2:
					//	prospectiveCharacter->Orientation=0;
					//	this->board->Character=prospectiveCharacter;
					//	//this->MoveCharacter();
					//	break;
					default:
						this->FinishLostGame();
						break;
					}
					break;
				default:
					this->FinishLostGame();
					break;
				}
				break;
			default:
				this->FinishLostGame();
				break;
			}
		}
		
		this->game->LastMovementTime = cv::getTickCount();
	}

	void GameController::CheckGameState(GameState expected)
	{
		if(this->game->State != expected)
		{
			throw new std::exception("Illegal game state!");
		}
	}

	void GameController::PrintTimeLeft(void)
	{;
		std::cout << "\r  Left: ";

		if(this->game->GetTimeUntilMovementStart() > 0)
		{
			std::cout << this->game->GetTimeUntilMovementStart();
		} 
		else if(this->game->GetTimeUntilNextMovement() > 0)
		{
			std::cout << this->game->GetTimeUntilNextMovement();
		}
		
		std::cout << " | Elapsed: " << this->game->GetElapsedTime() << "     ";
	}
}}